// SelectionSet.h
//
#ifndef SELECTION_SET_H
#define SELECTION_SET_H

#include <list>

namespace IDS{
namespace IDEAS{
namespace OpenGLView{


//---------------------------------------------------------------------------------------------
/**
  * Selection Policy.
  * Enumerator containing selection policies
  *
  * @author D. Vacca
  * @updated 14-sep-2009
  */

enum SelectionPolicy
{
	PolicyNormal = 0,   // Normal selection (one object at time)
	PolicyArea,         // Area selection
	PolicyExclusiveOR,  // Shift selection
	PolicyInclusiveOR   // Control selection
};

enum SelectionFulFillMode
{
	FulFillModeAtLeastOne = 0,
	FulFillModeAll
};


// --------------------------------------------------------------------------------------------
// Simple structure to handle multiple requests
template <typename T>
class SelectionRequest
{
public:
	// --------------------------------------------------------------------------------------------
	std::vector<T> types;
	std::vector<int> reqQty;
	std::vector<int> currQty;
	int totObjectRequested;
	// --------------------------------------------------------------------------------------------
	
public:
	// --------------------------------------------------------------------------------------------
	SelectionRequest(T t, int qty)
	{
		// ignore quantities on negative values of qty

		types.clear();
		reqQty.clear();
		currQty.clear();

		types.push_back(t);
		reqQty.push_back(qty);
		currQty.push_back(qty);
		totObjectRequested = qty;
	}

	// --------------------------------------------------------------------------------------------
	SelectionRequest(std::vector<T> t, std::vector<int> qty)
	{
		types.clear();
		reqQty.clear();
		currQty.clear();
		totObjectRequested = 0;

		for(size_t i=0; i<t.size(); ++i)
		{
			types.push_back(t[i]);
			reqQty.push_back(qty[i]);
			currQty.push_back(qty[i]);
			totObjectRequested += qty[i];
		}
	}
};


//---------------------------------------------------------------------------------------------
/**
  * Selection Set class.
  * This class keeps track of the current selection set
  *
  * @author D. Vacca
  * @updated 14-sep-2009
  */

template <
typename IdentifierType,	// Identifier
typename ObjectRefType,		// usually std::tr1::shared_ptr<VObjectGL>
typename GeometricType>
class SelectionSet
{
public:
	typedef std::map<IdentifierType,ObjectRefType> SelectionList;	// list <Reference to viewobject>

private:
#if _DEBUG
public:
#endif
	SelectionList selectionList;
	SelectionPolicy selectionPolicy;

	std::vector<SelectionRequest<GeometricType> >		requests;
	int									totReqCount;	// how many objects are currently requested

public:
	SelectionSet() {clearSelection(true); setPolicy(PolicyNormal);}
	~SelectionSet(){};

	// --------------------------------------------------------------------------------------------
	// Add the desired object to the selectionlist.
	// Remove it if already present.
	bool addObject(IdentifierType objID, ObjectRefType obj, GeometricType geom ) //, SelectionPolicy policy = Add)
	{
		SelectionList::iterator it = selectionList.find(objID);
		
		bool added = false;
		if(totReqCount < 0) // accept any quantity
		{
			
			// Remove it if already inserted
			if( it != selectionList.end() )
			{
				if( !hasRequests() )
				{
					selectionList.erase(it);
					added = false;
				}
				else
				{
					// ----------------------------------------------------
					for(size_t i=0; i<requests.size(); ++i)
					{
						for(size_t j=0; j<requests[i].types.size(); ++j)
						{
							if( geom == requests[i].types[j] ) // accept only if it suits the requested Type
							{					
								selectionList.erase(it);
								added = false;
							}
						}
					}
					// ----------------------------------------------------
				}
			}
			else
			{
				// Insert the object
				if( !hasRequests() )
				{
					selectionList[objID] = obj;
					added = true;
				}
				else
				{
					// ----------------------------------------------------
					for(size_t i=0; i<requests.size(); ++i)
					{
						for(size_t j=0; j<requests[i].types.size(); ++j)
						{
							if( geom == requests[i].types[j] ) // accept only if it suits the requested Type
							{					
								selectionList[objID] = obj;
								added = true;
							}
						}
					}
					// ----------------------------------------------------
				}
			}
		}
		else if(totReqCount > 0)	
		{
			for(size_t i=0; i<requests.size(); ++i)
			{
				for(size_t j=0; j<requests[i].types.size(); ++j)
				{
					if((geom == requests[i].types[j] || requests[i].types[j] < 0) && requests[i].currQty[j] > 0) // accept only if it suits the request
					{
						requests[i].currQty[j]--;
						totReqCount--;
					
						selectionList[objID] = obj;
						added = true;
					}
				}
			}
			
			//if(obj->getGeometricType() == reqObj) // accept only if it suits the request
			//selectionList[obj->id] = obj;
			//currReqNum--;
			//added  = true;
		}

		return added;
	}

	// --------------------------------------------------------------------------------------------
	bool removeObject(IdentifierType objID,ObjectRefType obj,GeometricType geom)
	{
		bool removed = false;
		
		// there were requests?
		if( hasRequests() )
		{
			for(size_t i=0; i<requests.size(); ++i)
			{
				for(size_t j=0; j<requests[i].types.size(); ++j)
				{
					if(geom == requests[i].types[j] && requests[i].currQty[j] < requests[i].reqQty[j]) // accept only if it suits the request
					{
						// check if object is in the selection list
						SelectionList::iterator it= selectionList.find(objID);
						if(it != selectionList.end())
						{
							selectionList.erase( it );
							requests[i].currQty[j]++;
							totReqCount++;
							removed = true;
							break;
						}
					
						//selectionList[objID] = obj;
						//added = true;
					}
				}
			}
		}
		else
		{
			// check if object is in the selection list
			SelectionList::iterator it= selectionList.find(objID);
			if(it != selectionList.end())
			{
				selectionList.erase( it );
				removed = true;
			}

			if(totReqCount>0)
				totReqCount++;
		}

		// successfully removed?
		if( removed )
			obj->setSelected(false);

		return removed;
	}

	// --------------------------------------------------------------------------------------------
	SelectionList &getObjects()	{return selectionList;}

	// --------------------------------------------------------------------------------------------
	void addRequest(SelectionRequest<GeometricType> req)
	{
		if (totReqCount < 0)
			totReqCount = 0;

		for(size_t i=0; i<req.types.size(); ++i)
			if(req.reqQty[i] > 0)
				totReqCount += req.reqQty[i];
			else
				totReqCount = -1; //

		requests.push_back(req);
	}

	// --------------------------------------------------------------------------------------------
	void setDefaultRequest()
	{
		totReqCount = -1;
		requests.clear();
	}
	
	// --------------------------------------------------------------------------------------------
	// useful to re-apply requests without changing the selectionset
	void refresh()
	{
		SelectionList tmp;	
		selectionList.swap(tmp);
		for(SelectionList::iterator it = tmp.begin(); it != tmp.end(); ++it)
			addObject(it->first, it->second, it->second->getGeometricType());
	}


	// --------------------------------------------------------------------------------------------
	//void setRequest(std::vector<GeometricObjectType> types, std::vector<int> num)
	//{
	//	assert( types.size() == num.size() ); // invalid Selection request?

	//	// reset request
	//	reqObj.clear();
	//	reqNum.clear();
	//	currReqObj.clear();
	//	totalRequests = 0;
	//	
	//	// set the requested types
	//	for(size_t i=0; i<types.size(); ++i)
	//	{
	//		reqObj.push_back(types[i]);
	//		reqNum.push_back(num[i]);
	//		currReqObj.push_back(num[i]);
	//		totalRequests += num[i];
	//	}

	//	// something went wrong...reset the selection request
	//	if(totalRequests == 0)
	//	{
	//		reqObj.clear();
	//		reqNum.clear();
	//		currReqObj.clear();
	//		totalRequests = -1;
	//	}
	//}

	// --------------------------------------------------------------------------------------------
	bool isRequestFulfilled(SelectionFulFillMode mode, bool selectOnlyRequestedObjects = true)
	{
		bool fulfill = false;
		int objCount = 0;
		
		if(mode == FulFillModeAtLeastOne)
		{
			for(size_t i=0; i<requests.size(); ++i)
			{
				int k = 0;
				for(size_t j=0; j<requests[i].types.size(); ++j)
				{
					if(requests[i].currQty[j] == 0)
						k += requests[i].reqQty[j];
				}

				if( k == requests[i].totObjectRequested || requests[i].totObjectRequested < 0 )
				{
					fulfill = true;
					objCount = k;
					break;
				}
			}
		}
		else if(mode == FulFillModeAll)
		{
			// @TODO:
			//fulfill = true;
			//for(size_t i=0; i<requests.size(); ++i)
			//{
			//	for(size_t j=0; j<requests[i].types.size(); ++j)
			//	{
			//		if(requests[i].currQty[j] != 0)
			//		{
			//			fulfill = false;
			//			break;
			//		}
			//		else
			//			objCount += requests[i].reqQty[j];
			//	}
			//}
		}

		if (selectOnlyRequestedObjects)
		{
			if( selectionList.size() == 0		|| 
				selectionList.size() != objCount)
				fulfill = false;
		}
		else
		{
			if( selectionList.size() == 0 )
				fulfill = false;
		}

		return fulfill;
	}

	// --------------------------------------------------------------------------------------------
	void clearSelection(bool clearRequests)
	{
		// dependent on Viewobject.
		// may be nicer to move this <for> elsewhere
		//for(SelectionList::iterator it = selectionList.begin(); it != selectionList.end(); ++it)
		//	it->second->setSelected(false);

		selectionList.clear();
		
		if(clearRequests)
			setDefaultRequest();
		else
			restoreRequest();
	}

	// --------------------------------------------------------------------------------------------
	void restoreRequest()
	{
		//requests.clear();
		totReqCount = 0;
		
		for(size_t i=0; i<requests.size(); ++i)
		{
			for(size_t j=0; j<requests[i].types.size(); ++j)
			{
				requests[i].currQty[j] = requests[i].reqQty[j];
				totReqCount += requests[i].reqQty[j];
			}
		}

		if(totReqCount == 0) 
			totReqCount = -1;
	}

	// --------------------------------------------------------------------------------------------
	bool hasRequests()						{return requests.size() >0;}
	// --------------------------------------------------------------------------------------------
	void setPolicy(SelectionPolicy policy)	{selectionPolicy = policy;}
	// --------------------------------------------------------------------------------------------
	SelectionPolicy getPolicy()				{return selectionPolicy;}
	// --------------------------------------------------------------------------------------------
	bool isEmpty()							{return selectionList.size() == 0;}
};




}}}

#endif